/**
 * @author: Tyler Rhodes
 * @version: 0.1
 *
 * Created: 05/09/2012
 */

package j2600;

import org.apache.log4j.*;

/** The CPU class of the atari 2600 processor. */
public class CPU {
  /** Logger class used for logging purposes */
  public static final Logger logger = Logger.getLogger(CPU.class);

  public static final short carry_flag     = 1 >> 0;
  public static final short zero_flag      = 1 >> 1;
  public static final short interrupt_flag = 1 >> 2;
  public static final short decimal_flag   = 1 >> 3;
  public static final short break_flag     = 1 >> 4;
  public static final short not_used_flag  = 1 >> 5;
  public static final short overflow_flag  = 1 >> 6;
  public static final short sign_flag      = 1 >> 7;
  /*
   * Private member variables
   * note: All byte variables are shorts, because they are actually unsigned.
   *       When modified, they need to be and'ed with 0xFF.
   */
  /** Accumulator for the processor */
  private short accumulator;
  /** x_index */
  private short x_index;
  /** y_index */
  private short y_index;
  /** Status register */
  private short status_reg;
  /** Program counter */
  private int pc;

  /** Memory */
  private Memory memory;

  /**
   * Constructor for the CPU class.
   */
  public CPU() {
      initialize();
  }
  /**
   * Constructor for the CPU class.
   * @param mem Memory object to use with this CPU class.
   */
  public CPU(Memory mem) {
      memory = mem;
      initialize();
  }

  /**
   * Sets up the CPU for usage.
   */
  private void initialize() {
      logger.info("Creating CPU");

      logger.info("Setting default values");
      //    pc = ?;
      accumulator = 0;
      x_index = 0;
      y_index = 0;
      status_reg = CPU.not_used_flag; // this is the only flag that is 1 always
  }

  /**
   * Set the zero flag, based on the given value
   * @param value Value to be checked.
   */
  private void setZeroFlag(short value) {
      setFlag(CPU.zero_flag, value == 0);
  }

  /**
   * Set the sign flag, based on the given value
   * @param value Value to be checked.
   */
  private void setSignFlag(short value) {
      char temp = (char) (value & 0xFF);
      setFlag(CPU.sign_flag, (temp < 0));
  }

  private void setCarryFlag(int value) {
      if (value != 0) {
        setFlag(CPU.carry_flag, true);
      }
      // TODO: does carry flag clear if not set?
  }

  /**
   * Set the overflow flag, based on the given value
   * @param value Value to be checked.
   */
  private void setOverflowFlag(short value) {
      setFlag(CPU.overflow_flag, (value & 0xFF00) != 0);
  }

  private boolean isFlagSet(short flag_mask) {
      return (status_reg & flag_mask) != 0;
  }

  private void setFlag(short flag_mask, boolean val) {
      if (val) {
        status_reg |= flag_mask;
      } else {
        status_reg &= (~flag_mask);
      }
  }

  private void orAccumulatorWith(short value) {
      accumulator |= value;

      setSignFlag(accumulator);
      setZeroFlag(accumulator);
  }
  private void andAccumulatorWith(short value) {
      accumulator &= value;

      setSignFlag(accumulator);
      setZeroFlag(accumulator);
  }

  private void xorAccumulatorWith(short value) {
      accumulator ^= value;

      setSignFlag(accumulator);
      setZeroFlag(accumulator);
  }
  private void testBitsWithAccumulator(short value) {
      setSignFlag(value);
      setOverflowFlag(value);

      setZeroFlag((short)(value & accumulator));
  }
  private void incrementAccumulatorWithCarry(short value) {
      short temp = (short) (value + accumulator + (isFlagSet(CPU.carry_flag) ? 1 : 0));
      setZeroFlag(temp);
      if (isFlagSet(CPU.decimal_flag)) {
        // this was taken from the zophar guide: http://www.zophar.net/fileuploads/2/10532krzvs/6502.txt
        if (((accumulator & 0xf) + (value & 0xf) + (isFlagSet(CPU.carry_flag) ? 1 : 0)) > 9)
          temp += 6;
        setSignFlag(temp);
        setFlag(CPU.overflow_flag,
                (((accumulator ^ value) & 0x80) == 0) &&
                (((accumulator ^ temp) & 0x80) != 0));
        if (temp > 0x99)
          temp += 96;
        setFlag(CPU.carry_flag, temp > 0x99);
      } else {
        setSignFlag(temp);
        setFlag(CPU.overflow_flag,
                (((accumulator ^ value) & 0x80) == 0) &&
                (((accumulator ^ temp) & 0x80) != 0));
        setFlag(CPU.carry_flag, temp > 0xFF);
      }

      accumulator = (short)(temp & 0xFF);
  }
  private short shiftLeft(short value) {
      value <<= 1;

      setSignFlag(value);
      setZeroFlag(value);
      setCarryFlag((value & 0x100) >> 8);

      value &= 0xff;
      return value;
  }
  private short shiftRight(short value) {
      setCarryFlag(value & 0x1);
      value >>= 1;

      value &= 0xff;

      setSignFlag(value);
      setZeroFlag(value);

      return value;
  }
  private short rotateLeft(short value) {
      value <<= 1;

      if (isFlagSet(CPU.carry_flag))
        value |= 0x1;
      setSignFlag(value);
      setZeroFlag(value);
      setCarryFlag((value & 0x100) >> 8);

      value &= 0xff;
      return value;
  }

  private short rotateRight(short value) {
      if (isFlagSet(CPU.carry_flag))
        value |= 0x100;
      setCarryFlag((value & 0x1));

      value >>= 1;

      setSignFlag(value);
      setZeroFlag(value);

      value &= 0xff;
      return value;
  }

  /**
   * Fetches, decodes, and executes next opcode
   * @return number of cycles it would have taken the CPU to perform the action
   */
  public int execute() {
      short opcode = memory.readByte(pc);
      short param1 = memory.readByte(pc + 1);
      short param2 = memory.readByte(pc + 2);
      int param_address = param1 | (param2 << 8);
      int num_cycles = 0;
      short value;

      if( logger.isDebugEnabled() )
      {
        logger.debug( "executing opcode: " + opcode );
        logger.debug( "pc:               " + pc );
        logger.debug( "param1:           " + param1 );
        logger.debug( "param2:           " + param2);
        logger.debug( "param_address:    " + param_address );
      }

      switch (opcode) {
          case 0x00: // BRK
            pc++;
            memory.stackPushShort(pc);
            status_reg |= CPU.break_flag;
            memory.stackPushByte(status_reg);
            status_reg |= CPU.interrupt_flag;
            pc = memory.readShort(0xFFFE);
            num_cycles = 7;
            break;

          case 0x01: // ORA - (Indirect,X)
            pc += 3;
            value = memory.preIndexedIndirect(param1, x_index);
            orAccumulatorWith(value);
            num_cycles = 6;
            break;

          case 0x05: // ORA - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            orAccumulatorWith(value);
            num_cycles = 3;
            break;

          case 0x06: // ASL - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            value = shiftLeft(value);
            memory.writeBackByte(value);
            num_cycles = 5;
            break;

          case 0x08: // PHP
            pc++;
            memory.stackPushByte(status_reg);
            num_cycles = 3;
            break;

          case 0x09: // ORA - Immediate
            pc += 2;
            value = param1;
            orAccumulatorWith(value);
            num_cycles = 2;
            break;

          case 0x0a: // ASL - Accumulator
            pc++;
            accumulator = shiftLeft(accumulator);
            num_cycles = 2;
            break;

          case 0x0d: // ORA - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            orAccumulatorWith(value);
            num_cycles = 4;
            break;

          case 0x0e: // ASL - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            value = shiftLeft(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;
          case 0x10: // BPL
            pc += 2;
            num_cycles = 2;
            if (!isFlagSet(CPU.sign_flag)) {
              int old_pc = pc;
              pc += param1;
              num_cycles += 1 + memory.pageBoundaryCycles(old_pc, pc);
            }
            break;

          case 0x11: // ORA - (Indirect),Y
            pc += 3;
            value = memory.postIndexedIndirect(param1, y_index);
            orAccumulatorWith(value);
            num_cycles = 5;
            break;

          case 0x15: // ORA - Zero Page,X
            pc += 3;
            value = memory.zeroPageIndexed(param1, x_index);
            orAccumulatorWith(value);
            num_cycles = 4;
            break;

          case 0x16: // ASL - Zero Page,X
            pc += 3;
            value = memory.zeroPageIndexed(param1, x_index);
            value = shiftLeft(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;

          case 0x18: // CLC
            pc++;
            setFlag(CPU.carry_flag, false);
            num_cycles = 2;
            break;

          case 0x19: // ORA - Absolute,Y
            pc += 3;
            value = memory.absoluteIndexed(param_address, y_index);
            orAccumulatorWith(value);
            num_cycles = 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x1d: // ORA - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            orAccumulatorWith(value);
            num_cycles = 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x1e: // ASL - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            value = shiftLeft(value);
            memory.writeBackByte(value);
            num_cycles = 7;
            break;

          case 0x20: // JSR
            memory.stackPushShort(pc);
            pc = param_address;
            num_cycles = 6;
            break;

          case 0x21: // AND - (Indirect,X)
            pc += 2;
            value = memory.preIndexedIndirect(param1, x_index);
            andAccumulatorWith(value);
            num_cycles = 6;
            break;

          case 0x24: // BIT - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            testBitsWithAccumulator(value);
            num_cycles = 3;
            break;

          case 0x25: // AND - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            andAccumulatorWith(value);
            num_cycles = 3;
            break;

          case 0x26: // ROL - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            value = rotateLeft(value);
            memory.writeBackByte(value);
            num_cycles = 5;
            break;

          case 0x28: // PLP
            pc += 1;
            status_reg = memory.stackPopByte();
            num_cycles = 4;
            break;

          case 0x29: // AND - Immediate
            pc += 2;
            andAccumulatorWith(param1);
            num_cycles = 2;
            break;

          case 0x2a: // ROL - Accumulator
            pc += 1;
            accumulator = rotateLeft(accumulator);
            num_cycles = 2;
            break;

          case 0x2c: // BIT - Absolute
            pc += 3;
            value = memory.absolute(param1);
            testBitsWithAccumulator(value);
            num_cycles = 4;
            break;

          case 0x2d: // AND - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            andAccumulatorWith(value);
            num_cycles = 4;
            break;

          case 0x2e: // ROL - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            value = rotateLeft(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;
          case 0x30: // BMI
            pc += 2;
            num_cycles = 2;
            if (isFlagSet(CPU.sign_flag)) {
              pc += param1;
              num_cycles += 1;
            }
            break;

          case 0x31: // AND - (Indirect),Y
            pc += 2;
            value = memory.postIndexedIndirect(param1, y_index);
            andAccumulatorWith(value);
            num_cycles = 5;
            break;

          case 0x35: // AND - Zero Page,X
            pc += 2;
            value = memory.zeroPageIndexed(param1, x_index);
            andAccumulatorWith(value);
            num_cycles = 4;
            break;

          case 0x36: // ROL - Zero Page,X
            pc += 2;
            value = memory.zeroPageIndexed(param1, x_index);
            value = rotateLeft(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;

          case 0x38: // SEC
            pc++;
            setFlag(CPU.carry_flag, true);
            num_cycles = 2;
            break;

          case 0x39: // AND - Absolute,Y
            pc += 3;
            value = memory.absoluteIndexed(param_address, y_index);
            andAccumulatorWith(value);
            num_cycles = 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x3d: // AND - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            andAccumulatorWith(value);
            num_cycles = 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x3e: // ROL - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            value = rotateLeft(value);
            memory.writeBackByte(value);
            num_cycles = 7;
            break;

          case 0x40: // RTI
            pc++;
            status_reg = memory.stackPopByte();
            pc = memory.stackPopShort();
            num_cycles = 6;
            break;

          case 0x41: // EOR - (Indirect,X)
            pc += 2;
            value = memory.preIndexedIndirect(param1, x_index);
            xorAccumulatorWith(value);
            num_cycles += 6;
            break;

          case 0x45: // EOR - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            xorAccumulatorWith(value);
            num_cycles += 3;
            break;

          case 0x46: // LSR - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            value = shiftRight(value);
            memory.writeBackByte(value);
            num_cycles = 5;
            break;

          case 0x48: // PHA
            pc++;
            memory.stackPushByte(accumulator);
            num_cycles = 3;
            break;

          case 0x49: // EOR - Immediate
            pc += 2;
            xorAccumulatorWith(param1);
            num_cycles += 2;
            break;

          case 0x4a: // LSR - Accumulator
            pc += 1;
            accumulator = shiftRight(accumulator);
            num_cycles = 2;
            break;

          case 0x4c: // JMP - Absolute
            pc = param_address;
            num_cycles = 3;
            break;

          case 0x4d: // EOR - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            xorAccumulatorWith(value);
            num_cycles += 4;
            break;

          case 0x4e: // LSR - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            value = shiftRight(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;

          case 0x50: // BVC
            pc += 2;
            num_cycles = 2;
            if (!isFlagSet(CPU.overflow_flag)) {
              int old_pc = pc;
              pc += param1;
              num_cycles += 1 + memory.pageBoundaryCycles(old_pc, pc);
            }
            break;

          case 0x51: // EOR - (Indirect),Y
            pc += 2;
            value = memory.postIndexedIndirect(param1, y_index);
            xorAccumulatorWith(value);
            num_cycles += 5 + memory.pageBoundaryCycles(pc);
            break;

          case 0x55: // EOR - Zero Page,X
            pc += 2;
            value = memory.zeroPageIndexed(param1, x_index);
            xorAccumulatorWith(value);
            num_cycles += 4;
            break;

          case 0x56: // LSR - Zero Page,X
            pc += 2;
            value = memory.zeroPageIndexed(param1, x_index);
            value = shiftRight(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;

          case 0x58: // CLI
            pc += 1;
            setFlag(CPU.interrupt_flag, false);
            num_cycles = 2;
            break;

          case 0x59: // EOR - Absolute,Y
            pc += 3;
            value = memory.absoluteIndexed(param_address, y_index);
            xorAccumulatorWith(value);
            num_cycles += 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x5d: // EOR - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            xorAccumulatorWith(value);
            num_cycles += 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x5e: // LSR - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param1, x_index);
            value = shiftRight(value);
            memory.writeBackByte(value);
            num_cycles = 7;
            break;

          case 0x60: // RTS
            pc = memory.stackPopByte() + 1;
            num_cycles = 6;
            break;

          case 0x61: // ADC - (Indirect,X)
            pc += 2;
            value = memory.preIndexedIndirect(param1, x_index);
            incrementAccumulatorWithCarry(value);
            num_cycles = 6;
            break;

          case 0x65: // ADC - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            incrementAccumulatorWithCarry(value);
            num_cycles = 3;
            break;

          case 0x66: // ROR - Zero Page
            pc += 2;
            value = memory.zeroPageAbsolute(param1);
            value = rotateRight(value);
            memory.writeBackByte(value);
            num_cycles = 5;
            break;

          case 0x68: // PLA
            pc++;
            accumulator = memory.stackPopByte();
            num_cycles = 4;
            break;

          case 0x69: // ADC - Immediate
            pc += 2;
            incrementAccumulatorWithCarry(param1);
            num_cycles = 2;
            break;

          case 0x6a: // ROR - Accumulator
            pc += 1;
            accumulator = rotateRight(accumulator);
            num_cycles = 2;
            break;

          case 0x6c: // JMP - Indirect
            pc = memory.readShort(param_address);
            num_cycles = 5;
            break;

          case 0x6d: // ADC - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            incrementAccumulatorWithCarry(value);
            num_cycles = 4;
            break;

          case 0x6e: // ROR - Absolute
            pc += 3;
            value = memory.absolute(param_address);
            value = rotateRight(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;

          case 0x70: // BVS
            pc += 2;
            num_cycles = 2;
            if (isFlagSet(CPU.overflow_flag)) {
              int old_pc = pc;
              pc += param1;
              num_cycles += 1 + memory.pageBoundaryCycles(old_pc, pc);
            }
            break;

          case 0x71: // ADC - (Indirect),Y
            pc += 2;
            value = memory.postIndexedIndirect(param1, y_index);
            incrementAccumulatorWithCarry(value);
            num_cycles = 5 + memory.pageBoundaryCycles(pc);
            break;

          case 0x75: // ADC - Zero Page,X
            pc += 2;
            value = memory.zeroPageIndexed(param1, x_index);
            incrementAccumulatorWithCarry(value);
            num_cycles = 4;
            break;

          case 0x76: // ROR - Zero Page,X
            pc += 2;
            value = memory.zeroPageIndexed(param1, x_index);
            value = rotateRight(value);
            memory.writeBackByte(value);
            num_cycles = 6;
            break;

          case 0x78: // SEI
            pc++;
            setFlag(CPU.interrupt_flag, true);
            num_cycles = 2;
            break;

          case 0x79: // ADC - Absolute,Y
            pc += 3;
            value = memory.absoluteIndexed(param_address, y_index);
            incrementAccumulatorWithCarry(value);
            num_cycles = 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x7d: // ADC - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            incrementAccumulatorWithCarry(value);
            num_cycles = 4 + memory.pageBoundaryCycles(pc);
            break;

          case 0x7e: // ROR - Absolute,X
            pc += 3;
            value = memory.absoluteIndexed(param_address, x_index);
            value = rotateRight(value);
            memory.writeBackByte(value);
            num_cycles = 7;
            break;

          case 0x81: // STA - (Indirect,X)
            pc += 2;
            memory.preIndexedIndirect(param1, x_index);
            memory.writeBackByte(accumulator);
            num_cycles = 6;
            break;

          case 0x84: // STY - Zero Page
            pc += 2;
            memory.zeroPageAbsolute(param1);
            memory.writeBackByte(y_index);
            num_cycles = 3;
            break;

          case 0x85: // STA - Zero Page
            pc += 2;
            memory.zeroPageAbsolute(param1);
            memory.writeBackByte(accumulator);
            num_cycles = 3;
            break;

          case 0x86: // STX - Zero Page
            pc += 2;
            memory.zeroPageAbsolute(param1);
            memory.writeBackByte(x_index);
            num_cycles = 3;
            break;

          case 0x88: // DEY
            pc++;
            y_index--;
            y_index &= 0xFF;
            setSignFlag(y_index);
            setZeroFlag(y_index);
            num_cycles = 2;
            break;

          case 0x8a: // TXA
            pc++;
            setSignFlag(x_index);
            setZeroFlag(x_index);
            accumulator = x_index;
            num_cycles = 2;
            break;

          case 0x8c: // STY - Absolute
            pc += 3;
            memory.absolute(param_address);
            memory.writeBackByte(y_index);
            num_cycles = 4;
            break;

          case 0x8d: // STA - Absolute
            pc += 3;
            memory.absolute(param_address);
            memory.writeBackByte(accumulator);
            num_cycles = 4;
            break;

          case 0x8e: // STX - Absolute
            pc += 3;
            memory.absolute(param_address);
            memory.writeBackByte(x_index);
            num_cycles = 4;
            break;

          case 0x90: // BCC
            pc += 2;
            num_cycles = 2;
            if (!isFlagSet(CPU.carry_flag)) {
              int old_pc = pc-2; /* set it to the old value of the pc, and not the incremented value */
              pc += param1;
              num_cycles += 1 + memory.pageBoundaryCycles(old_pc, pc);
            }
            break;

          case 0x91: // STA - (Indirect),Y
            pc += 2;
            memory.postIndexedIndirect(param1, y_index);
            memory.writeBackByte(accumulator);
            num_cycles = 6;
            break;

          case 0x94: // STY - Zero Page,X
            pc += 2;
            memory.zeroPageIndexed(param1, x_index);
            memory.writeBackByte(y_index);
            num_cycles = 4;
            break;

          case 0x95: // STA - Zero Page,X
            pc += 2;
            memory.zeroPageIndexed(param1, x_index);
            memory.writeBackByte(accumulator);
            num_cycles = 4;
            break;

          case 0x96: // STX - Zero Page,Y
            pc += 2;
            memory.zeroPageIndexed(param1, y_index);
            memory.writeBackByte(x_index);
            num_cycles = 4;
            break;

          case 0x98: // TYA
            pc++;
            setSignFlag(y_index);
            setZeroFlag(y_index);
            accumulator = y_index;
            num_cycles = 2;
            break;

          case 0x99: // STA - Absolute,Y
            pc += 3;
            memory.absoluteIndexed(param_address, y_index);
            memory.writeBackByte(accumulator);
            num_cycles = 5;
            break;

          case 0x9a: // TXS
            pc++;
            memory.stackPushByte(x_index);
            num_cycles = 2;
            break;

          case 0x9d: // STA - Absolute,X
            pc += 3;
            memory.absoluteIndexed(param_address, x_index);
            memory.writeBackByte(accumulator);
            num_cycles = 5;
            break;

          case 0xa0: // LDY - Immediate
            pc += 2;
            y_index = param1;
            num_cycles = 2;
            break;

          case 0xa1: // LDA - (Indirect,X)
            pc += 2;
            accumulator = memory.preIndexedIndirect( param1, x_index );
            num_cycles = 6;
            break;

          case 0xa2: // LDX - Immediate
            pc += 2;
            x_index = param1;
            num_cycles = 2;
            break;

          case 0xa4: // LDY - Zero Page
            pc += 2;
            y_index = memory.zeroPageAbsolute( param1 );
            num_cycles = 3;
            break;

          case 0xa5: // LDA - Zero Page
            pc += 2;
            accumulator = memory.zeroPageAbsolute( param1 );
            num_cycles = 3;
            break;

          case 0xa6: // LDX - Zero Page
            pc += 2;
            x_index = memory.zeroPageAbsolute( param1 );
            num_cycles = 3;
            break;

          case 0xa8: // TAY
            pc++;
            y_index = accumulator;
            num_cycles = 2;
            break;

          case 0xa9: // LDA - Immediate
            pc += 2;
            x_index = param1;
            num_cycles = 2;
            break;

          case 0xaa: // TAX
            pc++;
            x_index = accumulator;
            num_cycles = 2;
            break;

          case 0xac: // LDY - Absolute
            pc += 3;
            y_index = memory.absolute( param_address );
            num_cycels = 4;
            break;

          case 0xad: // LDA - Absolute
            pc += 3;
            accumulator = memory.absolute( param_address );
            num_cycels = 4;
            break;

          case 0xae: // LDX - Absolute
            pc += 3;
            accumulator = memory.absolute( param_address );
            num_cycels = 4;
            break;

          case 0xb0: // BCS
            pc += 2;
            num_cycles = 2;
            if ( isFlagSet(CPU.carry_flag) ) {
              int old_pc = pc-2; /* set it to the old value of the pc, and not the incremented value */
              pc += param1;
              num_cycles = 2;
              num_cycles += 1 + memory.pageBoundaryCycles( old_pc, pc );
            }
            break;

          case 0xb1: // LDA - (Indirect),Y REVIEW!!!!!!
            pc += 2;
            accumulator = memory.postIndexedIndirect( param1, y_index );
            num_cycles = 5;
            num_cycles += memory.pageBoundaryCycles( pc-2, pc );
            break;

          case 0xb4: // LDY - Zero Page,X
            pc += 2;
            y_index = memory.zeroPageIndex( param1, x_index );
            num_cycles = 4;
            break;

          case 0xb5: // LDA - Zero Page,X
            pc += 2;
            accumulator = memory.zeroPageIndex( param1, x_index );
            num_cycles = 4;
            break;

          case 0xb6: // LDX - Zero Page,Y
            pc += 2;
            x_index = memory.zeroPageIndex( param1, y_index );
            num_cycles = 4;
            break;

          case 0xb8: // CLV
          case 0xb9: // LDA - Absolute,Y
          case 0xba: // TSX
          case 0xbc: // LDY - Absolute,X
          case 0xbd: // LDA - Absolute,X
          case 0xbe: // LDX - Absolute,Y
          case 0xc0: // Cpy - Immediate
          case 0xc1: // CMP - (Indirect,X)
          case 0xc4: // CPY - Zero Page
          case 0xc5: // CMP - Zero Page
          case 0xc6: // DEC - Zero Page
          case 0xc8: // INY
          case 0xc9: // CMP - Immediate
          case 0xca: // DEX
          case 0xcc: // CPY - Absolute
          case 0xcd: // CMP - Absolute
          case 0xce: // DEC - Absolute
          case 0xd0: // BNE
          case 0xd1: // CMP (Indirect),Y
          case 0xd5: // CMP - Zero Page,X
          case 0xd6: // DEC - Zero Page,X
          case 0xd8: // CLD
          case 0xd9: // CMP - Absolute,Y
          case 0xdd: // CMP - Absolute,X
          case 0xde: // DEC - Absolute,X
          case 0xe0: // CPX - Immediate
          case 0xe1: // SBC - (Indirect,X)
          case 0xe4: // CPX - Zero Page
          case 0xe5: // SBC - Zero Page
          case 0xe6: // INC - Zero Page
          case 0xe8: // INX
          case 0xe9: // SBC - Immediate
          case 0xea: // NOP
          case 0xec: // CPX - Absolute
          case 0xed: // SBC - Absolute
          case 0xee: // INC - Absolute
          case 0xf0: // BEQ
          case 0xf1: // SBC - (Indirect),Y
          case 0xf5: // SBC - Zero Page,X
          case 0xf6: // INC - Zero Page,X
          case 0xf8: // SED
          case 0xf9: // SBC - Absolute,Y
          case 0xfd: // SBC - Absolute,X
          case 0xfe: // INC - Absolute,X
            logger.info("Opcode not yet implemented: " + opcode);
            break;
          default:
            logger.error("Unknown opcode encountered: " + opcode);
      }
      if( logger.isDebugEnabled() )
        logger.debug( "num_cycles: " + num_cycles );
      return num_cycles;
  }
}

/*
 * Local Variables:
 * c-basic-offset: 2
 * End:
 */
